/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * https://lxqt.org
 *
 * Copyright: 2015 LXQt team
 * Authors:
 *  Balázs Béla <balazsbela[at]gmail.com>
 *  Paulo Lieuthier <paulolieuthier@gmail.com>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include "statusnotifierbutton.h"

#include <QDir>
#include <QFile>
#include <QApplication>
#include <QDrag>
#include "../panel/iukuipanelplugin.h"
#include "sniasync.h"
#include "../panel/customstyle.h"
#include <QDebug>
#include <KWindowEffects>
//#include <XdgIcon>

#define MIMETYPE "ukui/UkuiTaskBar"
#define ICON_ZOOM 0.348

StatusNotifierButton::StatusNotifierButton(QString service, QString objectPath, IUKUIPanelPlugin* plugin, QWidget *parent)
    : StatusNotifierButtonAbstract(parent),
    m_menu(nullptr),
    m_menuImporter(nullptr),
    m_status(PASSIVE),
    m_fallbackIcon(QIcon::fromTheme("application-x-executable")),
    m_plugin(plugin)
{
    this->setAcceptDrops(true);
    m_interface = new SniAsync(service, objectPath, QDBusConnection::sessionBus(), this);

    connect(m_interface, &SniAsync::NewIcon, this, &StatusNotifierButton::newIcon);
    connect(m_interface, &SniAsync::NewOverlayIcon, this, &StatusNotifierButton::newOverlayIcon);
    connect(m_interface, &SniAsync::NewAttentionIcon, this, &StatusNotifierButton::newAttentionIcon);
    connect(m_interface, &SniAsync::NewToolTip, this, &StatusNotifierButton::newToolTip);
    connect(m_interface, &SniAsync::NewStatus, this, &StatusNotifierButton::newStatus);

    hideAbleStatusNotifierButton();
    connect(this,&StatusNotifierButton::paramReady,this,[=]() {
        if(!this->m_id.isEmpty() && this->m_iconStatus && !m_paramInit) {
            emit layoutReady();
            m_paramInit = true;
        } else {
            if(this->m_id.isEmpty()) {
                if(m_count < 5)      //超过5次将不再获取
                    hideAbleStatusNotifierButton();
                m_count++;
            }
        }
    });


    /*Menu返回值：
        无菜单项返回 - "/NO_DBUSMENU"；
        有菜单项返回 - "/MenuBar",其他；
        x-sni注册的图标返回 - ""
    */
    m_interface->propertyGetAsync(QLatin1String("Menu"), [this] (QDBusObjectPath path) {
        if(path.path() != "/NO_DBUSMENU" && !path.path().isEmpty()) {
            m_menuImporter = new MenuImporter(m_interface->service(), path.path(), this);
            if(m_menuImporter) {
                connect(m_menuImporter, &MenuImporter::menuUpdated, this, &StatusNotifierButton::updataItemMenu);
            }
        }
    });

    m_interface->propertyGetAsync(QLatin1String("Status"), [this] (QString status) {
        newStatus(status);
    });

    m_interface->propertyGetAsync(QLatin1String("IconThemePath"), [this] (QString value) {
        m_themePath = value;
        //do the logic of icons after we've got the theme path
        refetchIcon(ACTIVE);
        refetchIcon(PASSIVE);
        refetchIcon(NEEDSATTENTION);
    });
    this->setProperty("useIconHighlightEffect", 0x10);
    newToolTip();
    systemThemeChanges();

    //设置按钮属性
    this->setProperty("useButtonPalette",true);
    this->setAutoRaise(true);
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setHoverBtnProperty();
}

StatusNotifierButton::~StatusNotifierButton()
{
    delete m_interface;
    if(m_themeSettings) {
        delete(m_themeSettings);
        m_themeSettings = NULL;
    }
}

void StatusNotifierButton::newIcon()
{
    refetchIcon(ACTIVE);
}

void StatusNotifierButton::newOverlayIcon()
{
    refetchIcon(ACTIVE);
}

void StatusNotifierButton::newAttentionIcon()
{
    refetchIcon(NEEDSATTENTION);
}

void StatusNotifierButton::refetchIcon(Status status)
{
    m_interface->propertyGetAsync(QLatin1String("IconThemePath"), [this] (QString value) {
            m_themePath = value;
    });

    QString nameProperty, pixmapProperty;
    if (status == ACTIVE) {
        nameProperty = QLatin1String("IconName");
        pixmapProperty = QLatin1String("IconPixmap");
    } else if (status == NEEDSATTENTION) {
        nameProperty = QLatin1String("AttentionIconName");
        pixmapProperty = QLatin1String("AttentionIconPixmap");
    } else {
        resetIcon();
        return;
    }

    m_interface->propertyGetAsync(nameProperty, [this, status, pixmapProperty] (QString iconName) {
        QIcon nextIcon;
        if (!iconName.isEmpty()) {
            if (QIcon::hasThemeIcon(iconName)) {
                nextIcon = QIcon::fromTheme(iconName);
            } else {
                if(m_themePath.isEmpty())
                    m_themePath = "/usr/share/icons/";
                QDir themeDir(m_themePath);
                if (themeDir.exists()) {
                    if (themeDir.exists(iconName + ".png")) {
                        nextIcon.addFile(themeDir.filePath(iconName + ".png"));
                    }

                    if (themeDir.cd("hicolor") || (themeDir.cd("icons") && themeDir.cd("hicolor"))) {
                        const QStringList sizes = themeDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                        for (const QString &dir : sizes) {
                            const QStringList dirs = QDir(themeDir.filePath(dir)).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                            for (const QString &innerDir : dirs) {
                                QString file = themeDir.absolutePath() + "/" + dir + "/" + innerDir + "/" + iconName + ".png";
                                if (QFile::exists(file)) {
                                    nextIcon.addFile(file);
                                }
                            }
                        }
                    }
                }
            }
            switch (status) {
                case ACTIVE:
                    m_icon = nextIcon;
                    break;
                case NEEDSATTENTION:
                    m_attentionIcon = nextIcon;
                    break;
            }
            resetIcon();
        } else {
            m_interface->propertyGetAsync(pixmapProperty, [this, status, pixmapProperty] (IconPixmapList iconPixmaps) {

                if (iconPixmaps.empty())
                    return;

                QIcon nextIcon;
                for (IconPixmap iconPixmap: iconPixmaps) {
                    if (!iconPixmap.bytes.isNull()) {
                        QImage image((uchar*) iconPixmap.bytes.data(), iconPixmap.width,
                                     iconPixmap.height, QImage::Format_ARGB32);
                        const uchar *end = image.constBits() + image.byteCount();
                        uchar *dest = reinterpret_cast<uchar*>(iconPixmap.bytes.data());
                        for (const uchar *src = image.constBits(); src < end; src += 4, dest += 4)
                            qToUnaligned(qToBigEndian<quint32>(qFromUnaligned<quint32>(src)), dest);

                        QString style = m_themeSettings->get(STYLE_NAME).toString();
                        if(style == STYLE_NAME_KEY_DARK || style == STYLE_NAME_KEY_DEFAULT) {
                            image = getBlackThemeIcon(image);
                        }
                        nextIcon.addPixmap(QPixmap::fromImage(image));
                    }
                }
                switch (status) {
                    case ACTIVE:
                        m_icon = nextIcon;
                        break;
                    case NEEDSATTENTION:
                        m_attentionIcon = nextIcon;
                        break;
                }
                resetIcon();
                QSize size = this->iconSize();
                m_iconPixmap = this->icon().pixmap(this->icon().actualSize(size));
            });
        }
    });
}

void StatusNotifierButton::newToolTip()
{
    m_interface->propertyGetAsync(QLatin1String("ToolTip"), [this] (ToolTip tooltip) {
        QString toolTipTitle = tooltip.title;
        if (!toolTipTitle.isEmpty()) {
            m_toolTipTitle = toolTipTitle;
            setToolTip(toolTipTitle);
        } else {
            m_interface->propertyGetAsync(QLatin1String("Title"), [this] (QString title) {
                // we should get here only in case the ToolTip.title was empty
                if (!title.isEmpty()) {
                    m_toolTipTitle = title;
                    setToolTip(title);
                }
            });
        }
    });
}

void StatusNotifierButton::newStatus(QString status)
{
    Status newStatus;
    if (status == QLatin1String("Passive"))
        newStatus = PASSIVE;
    else if (status == QLatin1String("Active"))
        newStatus = ACTIVE;
    else if(status == QLatin1String("NeedsAttention"))
        newStatus = NEEDSATTENTION;
    else
        newStatus = ACTIVE;

    if (m_status == newStatus)
        return;

    m_status = newStatus;
    resetIcon();
    emit layoutUpdate();
}

void StatusNotifierButton::contextMenuEvent(QContextMenuEvent* event)
{
    //XXX: avoid showing of parent's context menu, we are (optionaly) providing context menu on mouseReleaseEvent
    //QWidget::contextMenuEvent(event);
}

void StatusNotifierButton::mouseMoveEvent(QMouseEvent *e)
{
    if (e->button() == Qt::RightButton)
        return;
    if (!(e->buttons() & Qt::LeftButton))
        return;
    if ((e->pos() - m_dragStart).manhattanLength() < QApplication::startDragDistance())
        return;

    if (e->modifiers() == Qt::ControlModifier) {
        return;
    }

    if(this->acceptDrops()) {
        QDrag *drag = new QDrag(this);
        QPixmap iconPixmap;
        QIcon ico = icon();

        QString iconName = this->icon().name();
        if(!iconName.isEmpty()) {
            QSize size = this->iconSize();
            iconPixmap = ico.pixmap(ico.actualSize(size));
            QImage image = iconPixmap.toImage();
            if(m_themeSettings) { //深色模式下，拖拽提示图标反白
                QString style = m_themeSettings->get(STYLE_NAME).toString();
                if(style == STYLE_NAME_KEY_DARK || style == STYLE_NAME_KEY_DEFAULT) {
                    image = getBlackThemeIcon(image);
                    iconPixmap = QPixmap::fromImage(image);
                }
            }
        } else {
            iconPixmap = m_iconPixmap;
            QImage image = m_iconPixmap.toImage();
            if(m_themeSettings) { //深色模式下，拖拽提示图标反白
                QString style = m_themeSettings->get(STYLE_NAME).toString();
                if(style == STYLE_NAME_KEY_DARK || style == STYLE_NAME_KEY_DEFAULT) {
                    image = getBlackThemeIcon(image);
                    iconPixmap = QPixmap::fromImage(image);
                }
            }
        }
        drag->setMimeData(mimeData());
        drag->setPixmap(iconPixmap);

        switch (m_plugin->panel()->position()) {
            case IUKUIPanel::PositionLeft:
            case IUKUIPanel::PositionTop:
                drag->setHotSpot(iconPixmap.rect().bottomLeft());
                break;
            case IUKUIPanel::PositionRight:
            case IUKUIPanel::PositionBottom:
                drag->setHotSpot(iconPixmap.rect().bottomRight());
                break;
        }

        //原按钮透明,取消文字和icon
        this->setIcon(QIcon());
        drag->exec();
        drag->deleteLater();
        refetchIcon(ACTIVE);
    }
    QToolButton::mouseMoveEvent(e);

    //QAbstractButton::mouseMoveEvent(e);
}

void StatusNotifierButton::mouseReleaseEvent(QMouseEvent *event)
{
    setHoverBtnProperty();

    if (event->button() == Qt::LeftButton)
        m_interface->Activate(QCursor::pos().x(), QCursor::pos().y());
    else if (event->button() == Qt::MidButton)
        m_interface->SecondaryActivate(QCursor::pos().x(), QCursor::pos().y());
    else if (Qt::RightButton == event->button()) {
        m_cursorLeftPos = QCursor::pos();
        if(m_menuImporter) {
            m_menuImporter->updateMenu();
        } else {
            m_interface->ContextMenu(m_cursorLeftPos.x(), m_cursorLeftPos.y());
        }
    }
    update();
    QToolButton::mouseReleaseEvent(event);
}

void StatusNotifierButton::wheelEvent(QWheelEvent *event)
{
    m_interface->Scroll(event->delta(), "vertical");
}

void StatusNotifierButton::resetIcon()
{
    if(m_status == PASSIVE) {
        this->setVisible(false);
        emit layoutUpdate();
        return;
    } else if(m_status == ACTIVE) {
        if(!m_icon.isNull())
            setIcon(m_icon);
        else if(!m_overlayIcon.isNull())
            setIcon(m_overlayIcon);
        else {
            qInfo()<<"Failed to get icon! Id:"<<m_id<<" status:"<<m_status;
            setIcon(m_fallbackIcon);
        }
    } else if(m_status == NEEDSATTENTION) {
        if(!m_attentionIcon.isNull()) {
            setIcon(m_attentionIcon);
        } else {
            qInfo()<<"Failed to get icon! Id:"<<m_id<<" status:"<<m_status;
            setIcon(m_fallbackIcon);
        }
    } else {
        qInfo()<<"Failed to get icon! Id:"<<m_id<<" status:"<<m_status;
        setIcon(m_fallbackIcon);
    }

    if(m_actionArea == STORAGE) {
        m_foldState ? this->setVisible(false) : this->setVisible(true);
    } else {
        this->setVisible(true);
    }

    m_iconStatus=true;
    emit paramReady();
}

void StatusNotifierButton::systemThemeChanges()
{
    //主题变化
    const QByteArray styleId(ORG_UKUI_STYLE);
    if(QGSettings::isSchemaInstalled(styleId)) {
        m_themeSettings = new QGSettings(styleId);
        connect(m_themeSettings, &QGSettings::changed, this, [=] (const QString &key) {
            if(key == ICON_THEME_NAME) {
                //主题变化任务栏主动更新图标
                refetchIcon(ACTIVE);
            }
            if(key == STYLE_NAME) {
                //主题变化重新设置按钮背景颜色属性
                setHoverBtnProperty();
                refetchIcon(ACTIVE);
            }
        });
    }
}

void StatusNotifierButton::updataItemMenu()
{
    m_menu = m_menuImporter->menu();
    if (m_menu && !m_menu->isEmpty()) {
        m_plugin->willShowWindow(m_menu);
        m_menu->exec(m_menuImporter->menu()->actions(), m_plugin->panel()->calculatePopupWindowPos(m_cursorLeftPos, m_menu->sizeHint()).topLeft(), nullptr, this); //任务栏显示右键菜单
    } else {
        m_interface->ContextMenu(m_cursorLeftPos.x(), m_cursorLeftPos.y()); //应用显示右键菜单
    }
}

void StatusNotifierButton::dragMoveEvent(QDragMoveEvent * e)
{
    update();
//    if (e->mimeData()->hasFormat(MIMETYPE))
//        e->acceptProposedAction();
//    else
//        e->ignore();

}

void StatusNotifierButton::dragEnterEvent(QDragEnterEvent *e)
{
    e->acceptProposedAction();
    const StatusNotifierButtonMimeData *mimeData = qobject_cast<const StatusNotifierButtonMimeData*>(e->mimeData());
    if (mimeData && mimeData->button()) {
        emit switchButtons(mimeData->button(), this);
        emit sendTitle(mimeData->button()->hideAbleStatusNotifierButton());
    }
    QToolButton::dragEnterEvent(e);
}

void StatusNotifierButton::dragLeaveEvent(QDragLeaveEvent *e)
{
    update();  //拖拽离开wigget时，需要updata
    e->accept();
}

QMimeData * StatusNotifierButton::mimeData()
{
    StatusNotifierButtonMimeData *mimeData = new StatusNotifierButtonMimeData();
//    QByteArray ba;
//    mimeData->setData(mimeDataFormat(), ba);
    mimeData->setButton(this);
    return mimeData;
}

void StatusNotifierButton::mousePressEvent(QMouseEvent *e)
{
    setPressBtnProperty();
    if (e->button() == Qt::LeftButton ) {
        m_dragStart = e->pos();
        return;
    }
    QToolButton::mousePressEvent(e);
}

bool StatusNotifierButton::event(QEvent *e)
{
//    if(e->type() != QEvent::ToolTipChange && e->type()!=QEvent::HoverMove && e->type()!=QEvent::Paint &&
//            e->type() != QEvent::HoverLeave && e->type()!=QEvent::Paint &&e->type() != QEvent::DragMove &&
//            e->type() != QEvent::Leave && e->type()!=QEvent::Enter &&e->type() != QEvent::DragMove &&
//            e->type() != QEvent::Gesture && e->type() != QEvent::MouseButtonPress && e->type() != QEvent::MouseButtonRelease &&
//            e->type() != QEvent::GestureOverride && e->type() !=QEvent::HoverEnter && e->type() != QEvent::MouseMove &&
//            e->type() !=QEvent::ChildAdded   && e->type() != QEvent::DragEnter )
//        qDebug()<<e->type();

    if(e->type() == QEvent::ChildRemoved) {
        emit cleanSignal();
    }
    return QToolButton::event(e);
}

void StatusNotifierButton::resizeEvent(QResizeEvent *event)
{
    IUKUIPanel *panel = m_plugin->panel();
    this->setIconSize(QSize(panel->panelSize()*ICON_ZOOM,panel->panelSize()*ICON_ZOOM));
    QToolButton::resizeEvent(event);
}

void StatusNotifierButton::enterEvent(QEvent *event)
{
    update();
}

void StatusNotifierButton::leaveEvent(QEvent *event)
{
    update();
}

void StatusNotifierButton::paintEvent(QPaintEvent *event)
{
    if (m_plugin->panel()->isHorizontal()) {
        this->setFixedSize(m_plugin->panel()->panelSize()*0.7, m_plugin->panel()->panelSize()*0.7);
    } else {
        this->setFixedSize(m_plugin->panel()->panelSize()*0.7, m_plugin->panel()->panelSize()*0.7);
    }
    QToolButton::paintEvent(event);
}

QString StatusNotifierButton::hideAbleStatusNotifierButton()
{
    m_interface->propertyGetAsync(QLatin1String("Id"), [this] (QString title) {
        m_id = "";
        m_id = title;
        emit paramReady();
    });
    return m_id;
}

int StatusNotifierButton::getStatus()
{
    return m_status;
}

bool StatusNotifierButton::getFoldState()
{
    return m_foldState;
}

void StatusNotifierButton::setFoldState(bool status)
{
    m_foldState = status;
}

int StatusNotifierButton::getActionArea()
{
    return m_actionArea;
}

void StatusNotifierButton::setActionArea(int actionArea)
{
    m_actionArea = actionArea;
}

void StatusNotifierButton::setHoverBtnProperty()
{
    if(m_themeSettings) {
        QStringList allKeys = m_themeSettings->keys();
        if(allKeys.contains(STYLE_NAME)) {
            QPalette pal = qApp->palette();
            QColor col = pal.color(QPalette::Active, QPalette::ButtonText);
            QString styleName = m_themeSettings->get(STYLE_NAME).toString();
            if(styleName==STYLE_NAME_KEY_DARK || styleName==STYLE_NAME_KEY_BLACK) {
                col.setAlphaF(m_blackHoverBtnAlphaF);
            } else if(styleName==STYLE_NAME_KEY_LIGHT || styleName==STYLE_NAME_KEY_WHITE || styleName==STYLE_NAME_KEY_DEFAULT) {
                col.setAlphaF(m_lightHoverBtnAlphaF);
            }
            pal.setColor(QPalette::Button, col);
            this->setPalette(pal);
        }
    }
}

void StatusNotifierButton::setPressBtnProperty()
{
    if(m_themeSettings) {
        QStringList allKeys = m_themeSettings->keys();
        if(allKeys.contains(STYLE_NAME)) {
            QPalette pal = qApp->palette();
            QColor col = pal.color(QPalette::Active, QPalette::ButtonText);
            QString styleName = m_themeSettings->get(STYLE_NAME).toString();
            if(styleName==STYLE_NAME_KEY_DARK || styleName==STYLE_NAME_KEY_BLACK) {
                col.setAlphaF(m_blackPressBtnAlphaF);
            } else if(styleName==STYLE_NAME_KEY_LIGHT || styleName==STYLE_NAME_KEY_WHITE || styleName==STYLE_NAME_KEY_DEFAULT) {
                col.setAlphaF(m_lightPressBtnAlphaF);
            }
            pal.setColor(QPalette::Button, col);
            this->setPalette(pal);
        }
    }
}

QImage StatusNotifierButton::getBlackThemeIcon(QImage image)
{
    QColor(255,255,255);
    QColor standard (31,32,34);
    for (int x = 0; x < image.width(); x++) {
        for (int y = 0; y < image.height(); y++) {
            auto color = image.pixelColor(x, y);
            if (color.alpha() > 0) {
                if(qAbs(color.red()-standard.red())<20 && qAbs(color.green()-standard.green())<20 && qAbs(color.blue()-standard.blue())<20) {
                    color.setRed(255);
                    color.setGreen(255);
                    color.setBlue(255);
                    image.setPixelColor(x, y, color);
                } else {
                    image.setPixelColor(x, y, color);
                }
            }
        }
    }
    return image;
}
